我們可以想像眼前有一幅馬賽克拼貼畫,它由數萬個彩色磁磚構成,而對於數位影像來說,這些彩色磁磚所指的就是像素 (pixel)。
一張數位影像,本質上是由許多像素所組成的二維矩陣。每個像素都帶有一個值,其代表該位置的色彩資訊。
既然每個像素都儲存著顏色資訊,那我們該如何用數字來描述「紅色」、「天空的藍色」或「草地的綠色」呢?這就是色彩空間 (color space) 的任務。
色彩空間定義了一套規則,用來將顏色量化成數字。接下來,我們來認識幾個在電腦視覺中至關重要的色彩空間。
我們最熟悉的色彩空間。它基於加色模型,就像舞台上的紅、綠、藍三盞聚光燈,透過調整各自的亮度,可以混合出光譜中的各種顏色。
舉例來說:
與 RGB 色彩空間的概念相同,但順序變為藍、綠、紅色。由於一些歷史因素,OpenCV 所採用的是 BGR 色彩空間。
因此要注意的是,當使用 Matplotlib 等其他套件時,我們需要手動地將 BGR 轉換回 RGB 色彩空間,以免顏色異常。
跟 RGB 與 BGR 色彩空間相比,更接近人類感知顏色的方式。HSV 三個字母分別代表:
XYZ 是一個基礎的、設備無關的色彩空間,試圖將所有人類可見的顏色都包含在內。其中 Y 分量直接對應顏色的亮度。
L*U*V* 色彩空間則是由 XYZ 轉換而來,在這個色彩空間內,兩點之間的數學距離,能很好地對應到人類視覺感知到的顏色差異。
在 OpenCV 中,我們可以使用 cv2.cvtColor()
來轉換色彩空間。
import cv2
import numpy as np
img_path = 'flower.jpg'
bgr_image = cv2.imread(img_path)
# --- 1. 查看影像的基本屬性 ---
print(f"圖片的形狀 (高, 寬, 通道數): {bgr_image.shape}")
print(f"圖片的資料類型: {bgr_image.dtype}")
top_left_pixel = bgr_image[0, 0]
print(f"左上角像素的 BGR 值: {top_left_pixel}")
# --- 2. 分離 B, G, R 通道 ---
# 使用 cv2.split() 將三個通道分離
b, g, r = cv2.split(bgr_image)
# 為了能正確顯示單一通道(它們是灰階的),我們可以建立一個全黑的畫布,再把通道放上去
h, w, _ = bgr_image.shape
zeros = np.zeros((h, w), dtype="uint8")
# cv2.merge() 是 cv2.split() 的反向操作
blue_channel_img = cv2.merge([b, zeros, zeros])
green_channel_img = cv2.merge([zeros, g, zeros])
red_channel_img = cv2.merge([zeros, zeros, r])
cv2.imshow("Original Image", bgr_image)
cv2.imshow("Blue Channel", blue_channel_img)
cv2.imshow("Green Channel", green_channel_img)
cv2.imshow("Red Channel", red_channel_img)
# --- 3. 色彩空間轉換:BGR -> HSV ---
hsv_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2HSV)
# 分離 HSV 通道來觀察
h, s, v = cv2.split(hsv_image)
cv2.imshow("HSV Image", hsv_image) # HSV 直接顯示顏色會很奇怪
cv2.imshow("Hue Channel", h)
cv2.imshow("Saturation Channel", s)
cv2.imshow("Value (Brightness) Channel", v)
# --- 4. 其他色彩空間轉換 ---
# BGR -> XYZ
xyz_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2XYZ)
# BGR -> L*U*V*
luv_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2Luv) # L*U*V* 直接顯示顏色會很奇怪
# --- 5. 儲存圖片 ---
cv2.imwrite('original_image.jpg', bgr_image)
cv2.imwrite('blue_channel.jpg', blue_channel_img)
cv2.imwrite('green_channel.jpg', green_channel_img)
cv2.imwrite('red_channel.jpg', red_channel_img)
cv2.imwrite('hsv_image.jpg', hsv_image)
cv2.imwrite('hue_channel.jpg', h)
cv2.imwrite('saturation_channel.jpg', s)
cv2.imwrite('value_channel.jpg', v)
cv2.imwrite('xyz_image.jpg', xyz_image)
cv2.imwrite('luv_image.jpg', luv_image)
print("\n已完成所有影像處理。")
cv2.waitKey(0)
cv2.destroyAllWindows()
Original Image
Red Channel
Green Channel
Blue Channel
Hue
Saturation
Value
HSV
XYZ
L*U*V*
cv2.cvtColor(Image, ColorSpace)
cv2.COLOR_BGR2HSV
代表 BGR 轉為 HSVcv2.split(Image, Mv)
cv2.merge([Channel1, Channel2, Channel3, ...])
cv2.imwrite(ImgPath, Image, Quality)
HSV 與 L*U*V* 直接顯示顏色很奇怪的原因是:顯示器會將 HSV (L*U*V*) 的數值誤解為 RGB 數值,且 L*U*V* 的 U 與 V 可能為負或超過 255,所以通常建議 HSV (L*U*V*) 三個通道分開來查看。